﻿/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using Bio.Data.Interfaces;
using Bio.Views.Structure.Editor.Internal.Editing;
using Bio.Views.Structure.Editor.Internal.Rendering;
using Bio.Views.Structure.Editor.Internal.Rendering.Interfaces;
using Bio.Views.Structure.Editor.Internal.Selection.Interfaces;

namespace Bio.Views.Structure.Editor.Internal.Selection
{
    public abstract class StructureElementSelectionGroupBase : IStructureElementSelectionGroup
    {
        #region IStructureElementSelectionGroup Members

        public abstract int ID { get; }
        public abstract IStructureElementVisualComponent Visual { get; }

        public virtual bool HitTestElement(Point location)
        {
            return Element.HitTest(location, true);
        }

        public virtual void SelectElement()
        {
            Element.Select(false);
        }

        public virtual void DeselectElement()
        {
            Element.Deselect();
        }

        public virtual void MakeElementSelectable()
        {
            Element.MakeSelectable();
        }

        public virtual void MakeElementUnselectable()
        {
            Element.MakeUnselectable();
        }

        public virtual void DeleteSelection()
        {
        }

        public virtual void SnapHelixToOpeningBasePair()
        {
        }

        public virtual void SnapHelixToClosingBasePair()
        {
        }

        public virtual void SnapLoop()
        {
        }

        public virtual void MoveBy(Point delta)
        {
        }

        public virtual void MoveTo(Point location)
        {
        }

        public virtual void RotateBy(double angleInDegrees)
        {
        }

        public virtual void RotateTo(double angleInDegrees)
        {
        }

        public virtual Point RotationDelta()
        {
            return new Point(0, 0); //By default, the rotation reference is with respect to the origin
        }

        public virtual void ResizeTo(double newWidth, double newPadding)
        {
        }

        public virtual void EditProperties(StructureElementPropertyChangeSet changeSet, List<IBioSymbol> filter)
        {
        }

        #endregion

        #region IDraggableSecondaryStructureElement Members

        public bool IsDragging
        {
            get;
            private set;
        }

        public void StartDrag(Point startLocation)
        {
            if (!IsDragging)
            {
                IsDragging = true;
                DragLastLocation = startLocation;
                DragInitiationLocation = startLocation;
            }
        }

        public Point UpdateDrag(Point currentLocation)
        {
            if (IsDragging)
            {
                Point delta = new Point(currentLocation.X - DragInitiationLocation.X,
                    currentLocation.Y - DragInitiationLocation.Y);
                DragLastLocation = currentLocation;
                return delta;
            }
            else { return new Point(); }
        }

        public Point EndDrag(Point endLocation)
        {
            if (IsDragging)
            {
                UpdateDrag(endLocation);
                IsDragging = false;
                Point dragDelta = new Point(endLocation.X - DragInitiationLocation.X,
                    endLocation.Y - DragInitiationLocation.Y);

                if (dragDelta.X != 0 || dragDelta.Y != 0)
                    MoveBy(dragDelta);
                return dragDelta;
            }
            else { return new Point(); }
        }

        #endregion

        #region IRotateableSecondaryStructureElement Members

        public bool IsRotating
        {
            get;
            private set;
        }

        public void StartRotation(Point startLocation)
        {
            if (!IsRotating)
            {
                IsRotating = true;
                Point rotationReferenceDelta = RotationDelta();
                RotateInitiationVector = new Vector(startLocation.X + rotationReferenceDelta.X, startLocation.Y + rotationReferenceDelta.Y);
                RotateLastVector = RotateInitiationVector;
            }
        }

        public double UpdateRotation(Point currentLocation)
        {
            if (IsRotating)
            {
                Point rotationReferenceDelta = RotationDelta();
                Vector currentLocationVector = new Vector(currentLocation.X + rotationReferenceDelta.X, currentLocation.Y + rotationReferenceDelta.Y);
                RotateLastVector = currentLocationVector;
                double angleBetween = Vector.AngleBetween(RotateInitiationVector, RotateLastVector);
                return angleBetween;
            }
            else { return 0.0; }
        }

        public double EndRotation(Point endLocation)
        {
            if (IsRotating)
            {
                UpdateRotation(endLocation);
                IsRotating = false;

                double rotation = Vector.AngleBetween(RotateInitiationVector, RotateLastVector);
                if ((-360.0 <= rotation) && (rotation <= 360.0))
                    RotateBy(rotation);

                return rotation;
            }
            else { return 0.0; }
        }

        #endregion

        protected StructureElementSelectionGroupBase(StructureDrawingManager manager, IStructureElementVisualComponent element)
        {
            Debug.Assert(manager != null);
            Debug.Assert(element != null);
            Manager = manager;
            Element = element;
        }

        protected IStructureElementVisualComponent Element { get; private set; }
        protected StructureDrawingManager Manager { get; private set; }
        private Point DragLastLocation { get; set; }
        private Point DragInitiationLocation { get; set; }
        private Vector RotateLastVector { get; set; }
        private Vector RotateInitiationVector { get; set; }
    }
}
